package cn.dazhou.networkdemo

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.telecom.TelecomManager
import android.telephony.CellInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.telephony.TelephonyManager.CellInfoCallback
import android.telephony.UiccCardInfo
import androidx.annotation.RequiresPermission
import androidx.appcompat.app.AppCompatActivity.TELECOM_SERVICE
import androidx.core.app.ActivityCompat
import androidx.core.telephony.SubscriptionManagerCompat
import timber.log.Timber
import java.net.InetSocketAddress
import java.net.Socket
import java.util.concurrent.Executors
import kotlin.concurrent.thread

object NetworkUtil {

    /** 判断是否有可用网络，如果没有任何可用网络，返回true，否则返回false */
    @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
    @Suppress("DEPRECATION")
    fun hasActiveNetwork(): Boolean {
        val manager = App.app.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

        // 当关闭“数据连接”开关时，而且没有Wifi，则activeNetwork或activeNetworkInfo为null
        // 当sim卡过期不管有无信号，activeNetwork或activeNetworkInfo为null
        // 只要有sim卡或者连接上wifi，不论是否能连接互联网，都会返回true。
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Timber.i("Android6.0或以上版本网络信息：")
            // Android 6.0或以上
            /* 使用小米手机测试结果：连接上一个没插网线的wifi,NET_CAPABILITY_INTERNET返回为true，NET_CAPABILITY_VALIDATED返回为false,
               这说明NET_CAPABILITY_INTERNET与ActiveNetworkInfo.isConnected的功能一样，只要连上了sim卡或wifi，不管是否能连上互联网，都返回true。
               关闭wifi，使用公司apn卡，NET_CAPABILITY_INTERNET返回为true，NET_CAPABILITY_VALIDATED返回为false,此时是可以连接apn专网的，说明
               NET_CAPABILITY_VALIDATED是访问互联网来验证的，所以在使用APN卡时，不能使用NET_CAPABILITY_VALIDATED来判断网络是否OK。
               而且NET_CAPABILITY_VALIDATED也不是百分之百可靠，在公司的手机上，连接上一个没网的wifi，返回的也是true。
               总结就是：使用NET_CAPABILITY_INTERNET判断就可以了，然后再写代码连接公司服务器，能连接上就认为网络是OK的。
               但是实验的时候，发现公司的手机启动VPN之后，NET_CAPABILITY_INTERNET返回false，而NET_CAPABILITY_VALIDATED返回true，所以
               可以两个一起判断，只要其中一个为true，则结果为true
             */
            val activeNetwork = manager.activeNetwork
            if (activeNetwork != null) {
                val networkCapabilities = manager.getNetworkCapabilities(activeNetwork)
                val isInternet = networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
                val isValidated = networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ?: false
                val isWifi = networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ?: false
                val isCellular = networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ?: false
                val isVpn = networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_VPN) ?: false
                Timber.i("isInternet = $isInternet, isValidated = $isValidated, isWifi = $isWifi, isCellular = $isCellular, isVpn = $isVpn")
                Timber.i("networkCapabilities = $networkCapabilities")
//                return isInternet or isValidated
            } else {
                Timber.i("activeNetwork为null")
//                return false
            }
        }

        // Android 6.0以下
        // 返回有关当前活动的默认数据网络的详细信息。连接后，此网络是传出连接的默认路由。
        // 在启动网络流量之前，应始终检查isConnected（）。如果没有默认网络，则可能返回null。
        // 插着手机卡，连接上Wifi时，Wifi会变成活动网络。
        // 如果wifi网络不正常，而手机卡网络正常，活动网络是否还是wifi呢？经测试，答案：活动网络还是wifi。
        Timber.i("Android6.0以下版本网络信息：")
        val ani = manager.activeNetworkInfo
        if (ani != null) { // 未指定数据卡时为null(选择用于数据网络的SIM卡)。关闭移动数据网络时也为null
            Timber.i("extraInfo = ${ani.extraInfo}") // APN，如：3gwap或"Dazhou2105"（连Wifi时）。报告有关网络状态的额外信息（如果较低的网络层提供了这些信息）。
            Timber.i("reason = ${ani.reason}")  //如：connected。报告尝试建立连接失败的原因（如果有）。
            Timber.i("state = ${ani.state}") // 如：CONNECTED。报告网络的当前粗粒度状态。
            Timber.i("subtypeName = ${ani.subtypeName}")// 如：LTE。返回描述网络子类型的易于理解的名称。4G网络为LTE，5G为NR
            Timber.i("typeName = ${ani.typeName}")// 如：MOBILE。返回一个易于理解的名称，描述网络的类型，例如“ WIFI”或“ MOBILE”。
            Timber.i("isAvailable = ${ani.isAvailable}")
            Timber.i("isConnected = ${ani.isConnected}")  // 连Wifi时，就算wifi路由没插网线也会返回true。也就是说connected为true不代码可以访问互联网，要用ping的方式。
            Timber.i("isConnectedOrConnecting = ${ani.isConnectedOrConnecting}")
        } else {
            Timber.i("activeNetworkInfo为null")
        }
        return ani?.isConnected ?: false
    }

    /** 判断是否可以连接到服务器，也可以用来判断网络是否OK */
    fun isServerReachable(serverIp: String, serverPort: Int, callback: (Boolean) -> Unit) {
        // 先判断是否有activeNetwork再去连接服务器，如果无activeNetwork，则连接服务器肯定不通，所以这种情况就不要去连服务器了，浪费时间
        if (!hasActiveNetwork()) {
            Timber.i("无可用网络，服务器不可达")
            callback(false)
            return
        }

        thread {
            val timeout = 5000 // 连接超时时间为5秒，5秒都连不上服务器就可以认为网络不通
            val address = InetSocketAddress(serverIp, serverPort) // 如果serverIp是一个域名，则这里也会进行网络连接操作，即创建对象时就进行网络操作，它会通过DNS将域名解析为IP。
            val start = System.currentTimeMillis()
            try {
                Socket().use { socket ->
                    socket.connect(address, timeout)
                    Timber.i("${serverIp}:${serverPort}可达, 用时：${System.currentTimeMillis() - start}")
                    callback(true)
                }
            } catch (e: Exception) {
                Timber.e(e, "${serverIp}:${serverPort}不可达, 用时：${System.currentTimeMillis() - start}")
                callback(false)
            }
        }
    }

    /** 判断当前网络是2G、3G、4G还是5G，需要READ_PHONE_STATE权限 */
    fun get2G3G4G5G(): String {
        if (ActivityCompat.checkSelfPermission(App.app, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return "没有READ_PHONE_STATE权限，无法获取dataNetworkType"
        }

        val dataNetworkType = App.telephonyManager.dataNetworkType
        val whatG =  when (dataNetworkType) {
            TelephonyManager.NETWORK_TYPE_UNKNOWN -> "未知"
            TelephonyManager.NETWORK_TYPE_GPRS,
            TelephonyManager.NETWORK_TYPE_EDGE,
            TelephonyManager.NETWORK_TYPE_CDMA,
            TelephonyManager.NETWORK_TYPE_1xRTT,
            TelephonyManager.NETWORK_TYPE_IDEN,
            TelephonyManager.NETWORK_TYPE_GSM -> "2G"
            TelephonyManager.NETWORK_TYPE_UMTS,
            TelephonyManager.NETWORK_TYPE_EVDO_0,
            TelephonyManager.NETWORK_TYPE_EVDO_A,
            TelephonyManager.NETWORK_TYPE_HSDPA,
            TelephonyManager.NETWORK_TYPE_HSUPA,
            TelephonyManager.NETWORK_TYPE_HSPA,
            TelephonyManager.NETWORK_TYPE_EVDO_B,
            TelephonyManager.NETWORK_TYPE_EHRPD,
            TelephonyManager.NETWORK_TYPE_HSPAP,
            TelephonyManager.NETWORK_TYPE_TD_SCDMA -> "3G"
            //TelephonyManager.NETWORK_TYPE_IWLAN, //NETWORK_TYPE_IWLAN 表示的是通过 Wi-Fi 提供的 IMS (IP Multimedia Subsystem) 服务，它并不直接对应于2G、3G、4G或5G等蜂窝网络技术。IWLAN（Integrated Wireless Local Area Network）允许移动设备使用Wi-Fi网络来进行语音通话（VoWiFi, Voice over WiFi）和视频通话等IMS服务。
            //TelephonyManager.NETWORK_TYPE_LTE_CA, 这是隐藏的，无法访问
            TelephonyManager.NETWORK_TYPE_LTE -> "4G"
            TelephonyManager.NETWORK_TYPE_NR -> "5G"
            else -> "见鬼: $dataNetworkType"
        }

        // public static String getNetworkTypeName(@NetworkType int type
        val getNetworkTypeNameMethod = TelephonyManager::class.java.getMethod("getNetworkTypeName", Int::class.javaPrimitiveType)
        val networkTypeName = getNetworkTypeNameMethod.invoke(App.telephonyManager, dataNetworkType) as String

        return "$dataNetworkType is $networkTypeName is $whatG"
    }

    /**
     * 为任何情况返回适当的槽索引。如果该对象是用createForSubscriptionId创建的，则返回与所提供的subId关联的槽位索引。
     * 否则，返回与默认subId关联的槽位索引。如果未插入SIM卡，则返回默认SIM卡槽位索引。
     */
    fun getSlotIndex(): Int {
        // public int getSlotIndex()
        // 该函数的具体实现如下：
        /*
        public int getSlotIndex() {
            int slotIndex = SubscriptionManager.getSlotIndex(getSubId());
            if (slotIndex == SubscriptionManager.SIM_NOT_INSERTED) {
                slotIndex = SubscriptionManager.DEFAULT_SIM_SLOT_INDEX;
            }
            return slotIndex;
        }
        */
        // getSubId的具体实现如下：
        /*
        private int getSubId() {
            if (SubscriptionManager.isUsableSubIdValue(mSubId)) {
                return mSubId;
            }
            return SubscriptionManager.getDefaultSubscriptionId();
        }
        */
        val getSlotIndexMethod = TelephonyManager::class.java.getMethod("getSlotIndex")
        val slotIndex = getSlotIndexMethod.invoke(App.telephonyManager) as Int
        return slotIndex
    }

    /**
     * 返回默认语音订阅的SIM卡槽位索引。如果只支持数据不支持语音，则返回默认数据订阅的SIM卡槽位索引。
     * 经实验，SubscriptionManager.getDefaultSubscriptionId()返回的订阅ID非常不确定，所以最好不要用这个方法。比如我设置了默认数据为卡1，而默认语音和短信都不设置默认，在H30手机上返回订阅ID为卡2的ID。
     */
    fun getDefaultSimSlotIndex(): Int {
        // SubscriptionManager.getDefaultSubscriptionId()的文档说明如下：
        // 返回系统的默认订阅id。对于支持语音的设备，它将返回getDefaultVoiceSubscriptionId。对于仅限数据的设备，
        // 它将返回getDefaultDataSubscriptionId。错误时可能返回INVALID_SUBSCRIPT
        // 总结就是，如果支持语音，则返回语音的默认订阅id，即便你同时支持数据。如果只支数据的情况才返回数据的默认订阅id。
        val defaultSubscriptionId = SubscriptionManager.getDefaultSubscriptionId()
        if (defaultSubscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            return -2
        }
        return SubscriptionManagerCompat.getSlotIndex(defaultSubscriptionId)
    }

    /** 获取默认语音/短信SIM卡的卡槽索引 */
    fun getDefaultVoiceSimSlotIndex(): Int {
        // SubscriptionManager.getDefaultVoiceSubscriptionId()的文档说明如下：
        // 返回系统默认的语音订阅id。在只有数据的设备上或发生错误时，将返回INVALID_SUBSCRIPTION_ID。
        val defaultVoiceSubscriptionId = SubscriptionManager.getDefaultVoiceSubscriptionId()
        if (defaultVoiceSubscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            return -2
        }
        return SubscriptionManagerCompat.getSlotIndex(defaultVoiceSubscriptionId)
    }

    /** 获取默认短信SIM卡的卡槽索引 */
    fun getDefaultSmsSimSlotIndex(): Int {
        // SubscriptionManager.getDefaultSmsSubscriptionId()的文档说明如下：
        // 返回系统默认的短信订阅id。在只有数据的设备上或发生错误时，将返回INVALID_SUBSCRIPTION_ID。
        val defaultSmsSubscriptionId = SubscriptionManager.getDefaultSmsSubscriptionId()
        if (defaultSmsSubscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            return -2
        }
        return SubscriptionManagerCompat.getSlotIndex(defaultSmsSubscriptionId)
    }

    /** 获取默认数据SIM卡的卡槽索引 */
    fun getDefaultDataSimSlotIndex(): Int {
        // SubscriptionManager.getDefaultDataSubscriptionId()的文档说明如下：
        // 返回系统默认的数据订阅id。在只有语音的设备上或出现错误时，将返回INVALID_SUBSCRIPTION_ID
        val defaultDataSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId()
        if (defaultDataSubscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            return -2
        }
        return SubscriptionManagerCompat.getSlotIndex(defaultDataSubscriptionId)
    }



    /**
     * 判断是否打开移动网络，该API需要Android 8.0或以上系统才可以调用
     */
    @SuppressLint("NewApi")
    fun isDataEnabled(): Boolean {
        if (ActivityCompat.checkSelfPermission(App.app, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return false
        }
        return App.telephonyManager.isDataEnabled
    }

    private fun getDataStateName(dataState: Int): String {
        return when (dataState) {
            TelephonyManager.DATA_DISCONNECTED -> "DATA_DISCONNECTED"
            TelephonyManager.DATA_CONNECTING -> "DATA_CONNECTING"
            TelephonyManager.DATA_CONNECTED -> "DATA_CONNECTED"
            TelephonyManager.DATA_SUSPENDED -> "DATA_SUSPENDED"
            else -> "DATA_UNKNOWN"
        }
    }

    fun getDataState(): String {
        return getDataStateName(App.telephonyManager.dataState)
    }

    @SuppressLint("NewApi")
    fun getImei(): String? {
        // getImei()和getImei(slotIndex)是API 26(Android 8.0才出来的)，低版本不知道有没有
        if (Utils.isSharedUserIdSystemApp(App.app)) {
            try {
                return App.telephonyManager.imei
            } catch (e: Exception) {
                Timber.e(e, "无法获取imei")
                return null
            }
        } else {
            if (ActivityCompat.checkSelfPermission(App.app, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
                return "没有READ_PHONE_STATE权限"
            } else {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    return "Android10版本开始，非系统App无法获取imei"
                } else {
                    try {
                        return App.telephonyManager.imei
                    } catch (e: Exception) {
                        Timber.e(e, "无法获取imei")
                        return null
                    }
                }
            }
        }
    }

    fun getPhoneNumber(): CharSequence? {
        if (ActivityCompat.checkSelfPermission(App.app, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(App.app, Manifest.permission.READ_PHONE_NUMBERS) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(App.app, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return null
        }
        return App.telephonyManager.line1Number
    }

    fun checkNetworkType(context: Context): String {
        return when (App.telephonyManager.phoneType) {
            TelephonyManager.PHONE_TYPE_NONE -> "无蜂窝网络"
            TelephonyManager.PHONE_TYPE_GSM -> "当前网络为 GSM（或 LTE）"
            TelephonyManager.PHONE_TYPE_CDMA -> "当前网络为 CDMA"
            TelephonyManager.PHONE_TYPE_SIP -> "当前为 SIP 网络"
            else -> "未知网络类型"
        }
    }

    fun checkPhoneType(): Int {
        return App.telephonyManager.phoneCount // 注：双卡双待返回2，双卡单待返回1
    }

    fun getSignalStrength(): String {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { // P为Android 9.0
            val signalStrength = App.telephonyManager.signalStrength ?: return "无信号"
            return "level ${signalStrength.level}"
        } else {
            return "getSignalStrength()是Android9.0才出来的API"
        }
    }

    fun getSimState(): String {
        return when (App.telephonyManager.simState) {
            TelephonyManager.SIM_STATE_UNKNOWN -> "SIM_STATE_UNKNOWN"
            TelephonyManager.SIM_STATE_NOT_READY -> "SIM_STATE_NOT_READY"
            TelephonyManager.SIM_STATE_READY -> "SIM_STATE_READY"
            TelephonyManager.SIM_STATE_ABSENT -> "SIM_STATE_ABSENT"
            else -> "else"
        }
    }

    fun getIccid(): CharSequence {
        if (ActivityCompat.checkSelfPermission(App.app, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return "没有READ_PHONE_STATE权限"
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {// Q为Android 10.0
            if (Utils.isSharedUserIdSystemApp(App.app)) {
                return App.telephonyManager.simSerialNumber ?: "null" // 未插卡时为null
            } else {
                return "Android10或以上版本没有系统权限无法获取"
            }
        }
        return App.telephonyManager.simSerialNumber ?: "null" // 未插卡时为null
    }

    fun getIccid2(): CharSequence {
        if (!Utils.isSharedUserIdSystemApp(App.app)) {
            return "需要系统权限才可以获取"
        }

        if (App.telephonyManager.hasIccCard()) {
            // UICC卡是ICC卡的升级版，hasIccCard()用于判断传统物理SIM卡是否存在，只关心是否有物理SIM卡。而getUiccCardsInfo()返回的对象可以包含实体SIM卡和eSIM卡（即嵌入式SIM卡，嵌入到设备主板上的，无需拆卸或更换）。
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Q 为 Android 10
                var iccid = ""
                if (App.telephonyManager.uiccCardsInfo.isEmpty()) {
                    iccid = "uiccCards列表为空"
                } else {
                    App.telephonyManager.uiccCardsInfo.forEach { uiccCardInfo: UiccCardInfo ->
                        Timber.i("uiccCardInfo = $uiccCardInfo") // Api文档说在API33时过时，使用UiccPortInfo.getIccId()来获取iccid
                        iccid = uiccCardInfo.iccId ?: "iccId为空"
                    }
                }
                return iccid
            } else {
                return "该方式必须在Android10或以上版本使用"
            }
        } else {
            return "没有插入sim卡"
        }
    }

    fun getIccid3(): CharSequence {
        if (App.telephonyManager.hasIccCard()) {
            val sm = SubscriptionManager.from(App.app)
            if (ActivityCompat.checkSelfPermission(App.app, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
                val activeSubscriptionInfo = sm.getActiveSubscriptionInfo(SubscriptionManager.getDefaultDataSubscriptionId())
                return activeSubscriptionInfo?.iccId ?: ""
            } else {
                return "没有READ_PHONE_STATE权限"
            }
        } else {
            return "没有插入sim卡"
        }
    }

    /**
     * 这种方式需要API23（Android 6.0）才可用
     */
    fun getIccid4(): CharSequence {
        if (App.telephonyManager.hasIccCard()) {
            val telecomManager = App.app.getSystemService(TELECOM_SERVICE) as TelecomManager
            if (ActivityCompat.checkSelfPermission(App.app, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
                val phoneAccounts = telecomManager.callCapablePhoneAccounts
                if (phoneAccounts != null && phoneAccounts.isNotEmpty()) {
                    val iccId = phoneAccounts[0].id
                    return iccId
                } else {
                    return "没有可用的电话账户"
                }
            } else {
                return "没有READ_PHONE_STATE权限"
            }
        } else {
            return "没有插入sim卡"
        }
    }

    fun getImsi(): CharSequence {
        if (ActivityCompat.checkSelfPermission(App.app, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return "没有READ_PHONE_STATE权限"
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {// Q为Android 10.0
            if (Utils.isSharedUserIdSystemApp(App.app)) {
                return App.telephonyManager.subscriberId
            } else {
                return "Android10或以上版本没有系统权限无法获取"
            }
        }
        return App.telephonyManager.subscriberId
    }

    /**
     * 通过requestNetworkScan函数也可以得到蜂窝信息，这种方式需要Android9.0或以上
     */
    fun requestCellInfos() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10
            if (ActivityCompat.checkSelfPermission(App.app, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                // 获取蜂窝信息（包含频段、信号），只给一次结果
                App.telephonyManager.requestCellInfoUpdate(Executors.newSingleThreadExecutor(), object: CellInfoCallback() {
                    override fun onCellInfo(cellInfoList: MutableList<CellInfo>) {
                        cellInfoList.forEach { Timber.i("cellInfo=$it") }
                    }

                    override fun onError(errorCode: Int, detail: Throwable?) {
                        Timber.e("请求蜂窝数据时出现错误，errorCode=$errorCode")
                    }
                })
            }
        }
    }

    /**
     * 设置数据开关，需要系统权限
     */
    fun setDataEnabled(enabled: Boolean) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // O为Android 8.0
            if (Utils.isSharedUserIdSystemApp(App.app)) {
                App.telephonyManager.isDataEnabled = enabled
            } else {
                Timber.w("需要有系统权限才可以设置数据开关")
            }
        } else {
           Timber.i("需要Android9.0才能调用setDataEnabled")
        }
    }
}